iT邦幫忙

2024 iThome 鐵人賽

DAY 19
0

昨天討論了會員系統,我們今天我們進入實作環節。我們要選擇 JWT 來實作。

API 文件設計

我們先來設計一下關於會員的API。在一般的工作場景下,會分前後端,可能比較小的公司或團隊就不會分。API 設計文件通常就是讓前後端溝通的一種文件。

因為通常前後端,會一起開發。但如果是在工作的話,身為前端的我們沒辦法等到後端開發完再開工吧!?不然這樣雖然前面沒事做很爽,但是如果有時程壓力的話(通常都會有)等到後端完成後就時間就會變得很趕。

所以在開發之前建議先和後端討論好後把API文件開好,然後前端就可以開始依據文件先開始實作,最後再串接再一起測試。

註冊 (Register)

  • 路徑: /auth/register
  • 方法: POST

Request :

{
  "username": "john_doe",
  "email": "john@example.com",
  "password": "strongpassword",
  "account": "john123"
}

Response (成功):

{
  "message": "註冊成功",
  "user": {
    "id": "12345",
    "username": "john_doe",
    "email": "john@example.com",
    "account": "john123"
  }
}

Response (錯誤):

{
  "error": "註冊失敗,該 email 已經被使用。"
}

登入 (Login)

  • 路徑: /auth/login
  • 方法: POST

Request:

{
  "email": "john@example.com",
  "password": "strongpassword"
}

Response 範例 (成功):

{
  "message": "登入成功",
  "access_token": "your_jwt_token"
}

Response 範例 (錯誤):

{
  "error": "帳號或密碼不正確。"
}

取得使用者資訊 (Get User Info)

  • 路徑: /users/:userId
  • 方法: GET
  • 授權: Bearer Token

Response (成功):

{
  "id": "12345",
  "username": "john_doe",
  "email": "john@example.com",
  "account": "john123",
  "created_at": "2024-09-01T12:00:00Z"
}

Response (錯誤):

{
  "error": "授權失敗,請重新登入。"
}

修改使用者資訊 (Update User Info)

  • 路徑: /users/:userId
  • 方法: PUT
  • 授權: Bearer Token

Request :

{
  "username": "new_username",
  "email": "newemail@example.com",
  "account": "newaccount"
}

Response(成功):

{
  "message": "使用者資訊更新成功",
  "user": {
    "id": "12345",
    "username": "new_username",
    "email": "newemail@example.com",
    "account": "newaccount"
  }
}

Response(錯誤):

{
  "error": "更新失敗,email 已經被使用。"
}

Cursor 實作

JWT

首先我們需要先加上 JWT,先請 cur幫我們加上 JWT。

幫我加上 jwt 認證

[!Info] GPT 回應
根據你提供的需求,我將為你實作註冊、登入、取得使用者資訊和修改使用者資訊的 API。以下是相應的代碼實現:

1. 更新 auth.service.ts

src/auth/auth.service.ts 中,添加註冊和登入的邏輯:

import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { RegisterDto } from '../dto/register.dto';
import { User, UserDocument } from '../schemas/user.schema';
import { Model } from 'mongoose';
import { InjectModel } from '@nestjs/mongoose';
import { JwtPayload } from './jwt.payload';

@Injectable()
export class AuthService {
  constructor(
    @InjectModel(User.name) private userModel: Model<UserDocument>,
    private jwtService: JwtService,
  ) {}

  async register(registerDto: RegisterDto): Promise<any> {
    const existingUser = await this.userModel.findOne({ email: registerDto.email });
    if (existingUser) {
      throw new Error('註冊失敗,該 email 已經被使用。');
    }

    const createdUser = new this.userModel(registerDto);
    await createdUser.save();

    return {
      message: '註冊成功',
      user: {
        id: createdUser._id,
        username: createdUser.username,
        email: createdUser.email,
        account: createdUser.account,
      },
    };
  }

  async login(email: string, password: string): Promise<any> {
    const user = await this.userModel.findOne({ email });
    if (!user || user.password !== password) {
      throw new Error('帳號或密碼不正確。');
    }

    const payload: JwtPayload = { username: user.username };
    const access_token = this.jwtService.sign(payload);

    return {
      message: '登入成功',
      access_token,
    };
  }

  async validateUser(payload: JwtPayload): Promise<any> {
    return this.userModel.findOne({ username: payload.username });
  }
}

2. 更新 app.controller.ts

src/app.controller.ts 中,添加註冊和登入的路由:

import { Controller, Post, Body, UseGuards, Get, Param, Put } from '@nestjs/common';
import { AppService } from './app.service';
import { RegisterDto } from './dto/register.dto';
import { AuthService } from './auth/auth.service';
import { AuthGuard } from '@nestjs/passport';
import { User } from './schemas/user.schema';

@Controller('auth')
export class AppController {
  constructor(
    private readonly appService: AppService,
    private readonly authService: AuthService,
  ) {}

  @Post('register')
  async register(@Body() registerDto: RegisterDto): Promise<any> {
    return this.authService.register(registerDto);
  }

  @Post('login')
  async login(@Body() body: { email: string; password: string }): Promise<any> {
    return this.authService.login(body.email, body.password);
  }
}

@Controller('users')
export class UserController {
  constructor(private readonly authService: AuthService) {}

  @UseGuards(AuthGuard('jwt'))
  @Get(':userId')
  async getUserInfo(@Param('userId') userId: string): Promise<User> {
    const user = await this.authService.validateUser({ username: userId });
    if (!user) {
      throw new Error('授權失敗,請重新登入。');
    }
    return user;
  }

  @UseGuards(AuthGuard('jwt'))
  @Put(':userId')
  async updateUserInfo(@Param('userId') userId: string, @Body() updateData: Partial<RegisterDto>): Promise<any> {
    const existingUser = await this.authService.validateUser({ username: userId });
    if (!existingUser) {
      throw new Error('授權失敗,請重新登入。');
    }

    const updatedUser = await this.authService.userModel.findByIdAndUpdate(userId, updateData, { new: true });
    return {
      message: '使用者資訊更新成功',
      user: {
        id: updatedUser._id,
        username: updatedUser.username,
        email: updatedUser.email,
        account: updatedUser.account,
      },
    };
  }
}

3. 更新 app.module.ts

確保在 src/app.module.ts 中導入 UserController

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { MongooseModule } from '@nestjs/mongoose';
import { User, UserSchema } from './schemas/user.schema';
import { AuthModule } from './auth/auth.module';
import { UserController } from './app.controller'; // 新增

@Module({
  imports: [
    MongooseModule.forRoot('mongodb://localhost:29017/ITHelp'),
    MongooseModule.forFeature([{ name: User.name, schema: UserSchema }]),
    AuthModule,
  ],
  controllers: [AppController, UserController], // 新增
  providers: [AppService],
})
export class AppModule {}

4. 錯誤處理

在實際應用中,建議使用 NestJS 的異常過濾器來處理錯誤,這樣可以更好地管理錯誤響應。

這樣就完成了註冊、登入、取得使用者資訊和修改使用者資訊的 API 實作。請根據需要進行測試和調整。


JWT 幫我加上去了,但其實打開終端機看還是會有些錯誤,可以通過反覆詢問AI修復。雖然是錯誤但不是太難的錯誤。向下面的錯誤,其實你可以通過再問AI去玩正,一直到終端機沒有錯誤。

API 文件實作

我們剛剛寫了 API 文件,就是為了請 Cursor AI幫我們做出來。

(把文件貼上去) 幫我依照文件生成 API

[!Info] GPT 回應
根據你提供的需求,我將為你實作註冊、登入、取得使用者資訊和修改使用者資訊的 API。以下是相應的代碼實現:

1. 更新 auth.service.ts

src/auth/auth.service.ts 中,添加註冊和登入的邏輯:

import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { RegisterDto } from '../dto/register.dto';
import { User, UserDocument } from '../schemas/user.schema';
import { Model } from 'mongoose';
import { InjectModel } from '@nestjs/mongoose';
import { JwtPayload } from './jwt.payload';

@Injectable()
export class AuthService {
  constructor(
    @InjectModel(User.name) private userModel: Model<UserDocument>,
    private jwtService: JwtService,
  ) {}

  async register(registerDto: RegisterDto): Promise<any> {
    const existingUser = await this.userModel.findOne({ email: registerDto.email });
    if (existingUser) {
      throw new Error('註冊失敗,該 email 已經被使用。');
    }

    const createdUser = new this.userModel(registerDto);
    await createdUser.save();

    return {
      message: '註冊成功',
      user: {
        id: createdUser._id,
        username: createdUser.username,
        email: createdUser.email,
        account: createdUser.account,
      },
    };
  }

  async login(email: string, password: string): Promise<any> {
    const user = await this.userModel.findOne({ email });
    if (!user || user.password !== password) {
      throw new Error('帳號或密碼不正確。');
    }

    const payload: JwtPayload = { username: user.username };
    const access_token = this.jwtService.sign(payload);

    return {
      message: '登入成功',
      access_token,
    };
  }

  async validateUser(payload: JwtPayload): Promise<any> {
    return this.userModel.findOne({ username: payload.username });
  }
}

2. 更新 app.controller.ts

src/app.controller.ts 中,添加註冊和登入的路由:

import { Controller, Post, Body, UseGuards, Get, Param, Put } from '@nestjs/common';
import { AppService } from './app.service';
import { RegisterDto } from './dto/register.dto';
import { AuthService } from './auth/auth.service';
import { AuthGuard } from '@nestjs/passport';
import { User } from './schemas/user.schema';

@Controller('auth')
export class AppController {
  constructor(
    private readonly appService: AppService,
    private readonly authService: AuthService,
  ) {}

  @Post('register')
  async register(@Body() registerDto: RegisterDto): Promise<any> {
    return this.authService.register(registerDto);
  }

  @Post('login')
  async login(@Body() body: { email: string; password: string }): Promise<any> {
    return this.authService.login(body.email, body.password);
  }
}

@Controller('users')
export class UserController {
  constructor(private readonly authService: AuthService) {}

  @UseGuards(AuthGuard('jwt'))
  @Get(':userId')
  async getUserInfo(@Param('userId') userId: string): Promise<User> {
    const user = await this.authService.validateUser({ username: userId });
    if (!user) {
      throw new Error('授權失敗,請重新登入。');
    }
    return user;
  }

  @UseGuards(AuthGuard('jwt'))
  @Put(':userId')
  async updateUserInfo(@Param('userId') userId: string, @Body() updateData: Partial<RegisterDto>): Promise<any> {
    const existingUser = await this.authService.validateUser({ username: userId });
    if (!existingUser) {
      throw new Error('授權失敗,請重新登入。');
    }

    const updatedUser = await this.authService.userModel.findByIdAndUpdate(userId, updateData, { new: true });
    return {
      message: '使用者資訊更新成功',
      user: {
        id: updatedUser._id,
        username: updatedUser.username,
        email: updatedUser.email,
        account: updatedUser.account,
      },
    };
  }
}

3. 更新 app.module.ts

確保在 src/app.module.ts 中導入 UserController

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { MongooseModule } from '@nestjs/mongoose';
import { User, UserSchema } from './schemas/user.schema';
import { AuthModule } from './auth/auth.module';
import { UserController } from './app.controller'; // 新增

@Module({
  imports: [
    MongooseModule.forRoot('mongodb://localhost:29017/ITHelp'),
    MongooseModule.forFeature([{ name: User.name, schema: UserSchema }]),
    AuthModule,
  ],
  controllers: [AppController, UserController], // 新增
  providers: [AppService],
})
export class AppModule {}

4. 錯誤處理

在實際應用中,建議使用 NestJS 的異常過濾器來處理錯誤,這樣可以更好地管理錯誤響應。

這樣就完成了註冊、登入、取得使用者資訊和修改使用者資訊的 API 實作。請根據需要進行測試和調整。


按下套用讓Cursor 幫你修改程式碼,一樣會有一些錯誤,可以按下面 AI FIx in Chat 來回修改到沒有錯誤為止。

全部修完後還會出現這個問題,但不用擔心,一樣可以丟到AI修復。

結語

我覺得套用玩來回修復的過程會有一點折磨,因為其實有些如果你知道的話自己修改會比較快。AI有時候會和你鬼打牆。如果全部都靠AI的話一樣會有一些問題。但也不是不能完成任務,只是需要比較多耐心。

總之,AI 幫我們完成程式碼了。明天再來驗證一下他寫的對不對,有沒有照文件寫。


上一篇
[Day18] 會員認證系統簡介
下一篇
[DAY20] 確認與驗證AI生成的會員功能
系列文
30天 使用chatGPT輔助學習APP完成接案任務委託30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言